package com.qen.truelicense;

import com.qen.truelicense.license.AbstractKeyStoreParam;
import com.qen.truelicense.license.CipherParam;
import com.qen.truelicense.license.DefaultCipherParam;
import com.qen.truelicense.license.DefaultLicenseParam;
import com.qen.truelicense.license.KeyStoreParam;
import com.qen.truelicense.license.LicenseContent;
import com.qen.truelicense.license.LicenseContentException;
import com.qen.truelicense.license.LicenseManager;
import com.qen.truelicense.license.LicenseNotary;
import com.qen.truelicense.license.LicenseParam;
import com.qen.truelicense.license.NoLicenseInstalledException;
import com.qen.truelicense.xml.GenericCertificate;
import com.qen.truelicense.xml.XMLConstants;
import lombok.Builder;
import lombok.Data;

import javax.security.auth.x500.X500Principal;
import java.beans.XMLDecoder;
import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.text.MessageFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.prefs.Preferences;

/**
 * License生成类 -- 用于license生成
 */
@Deprecated
public class LicenseCreator {

    private final static X500Principal DEFAULT_HOLDER_AND_ISSUER = new X500Principal("CN=QEN, OU=QEN, O=QEN, L=TY, ST=SX, C=CN");

//    private static Logger logger = LogManager.getLogger(LicenseCreator.class);

    private LicenseObj license;

    public LicenseCreator(LicenseObj license) {
        this.license = license;
    }

    public static void main(String[] args) {
        LicenseCreator creator = new LicenseCreator(
                LicenseObj.builder()
                        .issued(new Date())
                        .privateAlias("privatekey")
                        .keyPass("qen123")
                        .storePass("qen123")
                        .subject("XUHUO")
                        .licensePath("/data/license.lic")
                        .privateKeysStorePath("/data/privatekeys.store")
                        .notBefore(new Date())
                        .consumerAmount(1)
                        .description("本地测试")
                        .licenseExtraModel(new HashMap<>() {
                            {
                                put("UserCount", 1);
                                put("MacAddress", "00:11:22:33:44:55");
                            }
                        })
                        .notAfter(StringUtils.str2Date(StringUtils.addDay(new Date(), 365), "yyyy-MM-dd"))
                        .build()
        );
        creator.generateLicense();
    }

    /**
     * 生成License证书
     */
    public boolean generateLicense() {
        try {
            LicenseManager licenseManager = new CustomLicenseManager(initLicenseParam());
            LicenseContent licenseContent = initLicenseContent();
            licenseManager.store(licenseContent, new File(license.getLicensePath()));
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            System.err.println(String.format(MessageFormat.format("证书生成失败：{0}", license)));
            return false;
        }
    }

    /**
     * 初始化证书生成参数
     */
    private LicenseParam initLicenseParam() {
        Preferences preferences = Preferences.userNodeForPackage(LicenseCreator.class);

        //设置对证书内容加密的秘钥
        CipherParam cipherParam = new DefaultCipherParam(license.getStorePass());

        KeyStoreParam privateStoreParam = new CustomKeyStoreParam(LicenseCreator.class
                , license.getPrivateKeysStorePath()
                , license.getPrivateAlias()
                , license.getStorePass()
                , license.getKeyPass());

        return new DefaultLicenseParam(license.getSubject()
                , preferences
                , privateStoreParam
                , cipherParam);
    }

    /**
     * 设置证书生成正文信息
     */
    private LicenseContent initLicenseContent() {
        LicenseContent licenseContent = new LicenseContent();
        licenseContent.setHolder(DEFAULT_HOLDER_AND_ISSUER);
        licenseContent.setIssuer(DEFAULT_HOLDER_AND_ISSUER);

        licenseContent.setSubject(license.getSubject());
        licenseContent.setIssued(license.getIssued());
        licenseContent.setNotBefore(license.getNotBefore());
        licenseContent.setNotAfter(license.getNotAfter());
        licenseContent.setConsumerType(license.getConsumerType());
        licenseContent.setConsumerAmount(license.getConsumerAmount());
        licenseContent.setInfo(license.getDescription());

        //扩展校验，这里可以自定义一些额外的校验信息(也可以用json字符串保存)
        if (license.getLicenseExtraModel() != null) {
            licenseContent.setExtra(license.getLicenseExtraModel());
        }

        return licenseContent;
    }

    @Data
    @Builder
    public static class LicenseObj {

        /**
         * 证书subject
         */
        private String subject;

        /**
         * 私钥别称
         */
        private String privateAlias;

        /**
         * 私钥密码（需要妥善保管，不能让使用者知道）
         */
        private String keyPass;

        /**
         * 访问私钥库的密码
         */
        private String storePass;

        /**
         * 证书生成路径
         */
        private String licensePath;

        /**
         * 私钥库存储路径
         */
        private String privateKeysStorePath;

        /**
         * 发布时间
         */
        private Date issued = new Date();
        /**
         * 证书生效时间
         */
        private Date notBefore;

        /**
         * 证书失效时间
         */
        private Date notAfter;

        /**
         * 用户类型
         */
        private String consumerType = "user";

        /**
         * 用户数量
         */
        private Integer consumerAmount = 1;

        /**
         * 描述信息
         */
        private String description = "";

        /**
         * 额外的服务器硬件等校验信息
         */
        private Map<String, Object> licenseExtraModel;
    }

    public static class CustomLicenseManager extends LicenseManager {

//    private static Logger logger = LogManager.getLogger(CustomLicenseManager.class);

        public CustomLicenseManager(LicenseParam param) {
            super(param);
        }

        /**
         * 复写create方法
         */
        @Override
        protected synchronized byte[] create(LicenseContent content, LicenseNotary notary) throws Exception {
            initialize(content);
            this.validateCreate(content);
            final GenericCertificate certificate = notary.sign(content);
            return getPrivacyGuard().cert2key(certificate);
        }

        /**
         * 复写install方法，其中validate方法调用本类中的validate方法，校验IP地址、Mac地址等其他信息
         */
        @Override
        protected synchronized LicenseContent install(final byte[] key, final LicenseNotary notary) throws Exception {
            final GenericCertificate certificate = getPrivacyGuard().key2cert(key);
            notary.verify(certificate);
            final LicenseContent content = (LicenseContent) this.load(certificate.getEncoded());
            this.validate(content);
            setLicenseKey(key);
            setCertificate(certificate);

            return content;
        }

        /**
         * 复写verify方法，调用本类中的validate方法，校验IP地址、Mac地址等其他信息
         */
        @Override
        protected synchronized LicenseContent verify(final LicenseNotary notary) throws Exception {

            // Load license key from preferences,
            final byte[] key = getLicenseKey();
            if (null == key) {
                throw new NoLicenseInstalledException(getLicenseParam().getSubject());
            }

            GenericCertificate certificate = getPrivacyGuard().key2cert(key);
            notary.verify(certificate);
            final LicenseContent content = (LicenseContent) this.load(certificate.getEncoded());
            this.validate(content);
            setCertificate(certificate);

            return content;
        }

        /**
         * 校验生成证书的参数信息
         */
        protected synchronized void validateCreate(final LicenseContent content) throws LicenseContentException {
            final LicenseParam param = getLicenseParam();
            final Date now = new Date();
            final Date notBefore = content.getNotBefore();
            final Date notAfter = content.getNotAfter();
            if (null != notAfter && now.after(notAfter)) {
                throw new LicenseContentException("证书失效时间不能早于当前时间");
            }
            if (null != notBefore && null != notAfter && notAfter.before(notBefore)) {
                throw new LicenseContentException("证书生效时间不能晚于证书失效时间");
            }
            final String consumerType = content.getConsumerType();
            if (null == consumerType) {
                throw new LicenseContentException("用户类型不能为空");
            }
        }


        /**
         * 复写validate方法，用于增加我们额外的校验信息
         */
        @Override
        protected synchronized void validate(final LicenseContent content) throws LicenseContentException {
            //1. 首先调用父类的validate方法
            super.validate(content);
            //2. 然后校验自定义的License参数，去校验我们的license信息
            Map<String, Object> expectedCheckModel = (Map<String, Object>) content.getExtra();
            // 做我们自定义的校验
        }


        /**
         * 重写XMLDecoder解析XML
         */
        private Object load(String encoded) {
            BufferedInputStream inputStream = null;
            XMLDecoder decoder = null;
            try {
                inputStream = new BufferedInputStream(new ByteArrayInputStream(encoded.getBytes(XMLConstants.XML_CHARSET)));
                decoder = new XMLDecoder(new BufferedInputStream(inputStream, XMLConstants.DEFAULT_BUFSIZE), null, null);
                return decoder.readObject();
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            } finally {
                try {
                    if (decoder != null) {
                        decoder.close();
                    }
                    if (inputStream != null) {
                        inputStream.close();
                    }
                } catch (Exception e) {
                    System.out.println(String.format("XMLDecoder解析XML失败%s", e));
                }
            }

            return null;
        }

    }

    public static class CustomKeyStoreParam extends AbstractKeyStoreParam {

        /**
         * 公钥/私钥在磁盘上的存储路径
         */
        private String storePath;
        private String alias;
        private String storePwd;
        private String keyPwd;

        public CustomKeyStoreParam(Class clazz, String resource, String alias, String storePwd, String keyPwd) {
            super(clazz, resource);
            this.storePath = resource;
            this.alias = alias;
            this.storePwd = storePwd;
            this.keyPwd = keyPwd;
        }


        @Override
        public String getAlias() {
            return alias;
        }

        @Override
        public String getStorePwd() {
            return storePwd;
        }

        @Override
        public String getKeyPwd() {
            return keyPwd;
        }

        /**
         * AbstractKeyStoreParam里面的getStream()方法默认文件是存储的项目中。
         * 用于将公私钥存储文件存放到其他磁盘位置而不是项目中
         */
        @Override
        public InputStream getStream() throws IOException {
//        return new FileInputStream(new File(storePath));
            File file = new File(storePath);
            if (file.exists()) {
                return new FileInputStream(file);
            } else {
                throw new FileNotFoundException(storePath);
            }
        }
    }
}

